/**
 * @file Config.h
 * @author  haifeng (haifengxiao@icloud.com)
 * @brief
 * @version 0.1
 * @date 2021-07-17
 *
 * @copyright Copyright (c) 2021
 *
 */

#pragma once

#include <map>
#include <set>
#include <unordered_set>
#include <unordered_map>
#include <string>
#include <sstream>
#include <memory>
#include <yaml-cpp/yaml.h>

#include "ConfigVal.h"
#include "ConfigValBase.h"

namespace Chen {
    namespace Conf {

        class Config {
        public:
            typedef std::map<std::string, ConfigValBase::ptr> ConfigMap;

            /**
             * @brief
             *
             * @tparam T
             * @param name
             * @param default_value
             * @param description
             * @return ConfigVal<T>::ptr
             */
            template <typename T>
            static typename ConfigVal<T>::ptr Lookup(const std::string& name
                , const T default_value, const std::string& description = "")     {
                auto tmp = Lookup<T>(name);

                if (tmp != nullptr) {
                    CHEN_LOG_INFO(CHEN_LOG_ROOT()) << "Lookup" << name << "exits";
                    return tmp;
                }
                if (name.find_first_not_of("abcdefghijklmnopqrstuvwxyz._012345678") != std::string::npos) {
                    CHEN_LOG_ERROR(CHEN_LOG_ROOT()) << "Lookup name invaild" << name;
                    throw std::invalid_argument(name);
                }
                typename ConfigVal<T>::ptr value(new ConfigVal<T>(name, default_value, description));
                s_datas[name] = value;
                return value;
            }

            /**
             * @brief
             *
             * @tparam T
             * @param name
             * @return ConfigVal<T>::ptr
             */
            template <typename T>
            static typename ConfigVal<T>::ptr Lookup(const std::string& name)     {
                auto it = s_datas.find(name);
                if (it == s_datas.end()) {
                    return nullptr;
                }
                return std::dynamic_pointer_cast<ConfigVal<T>>(it->second);
            }

            /**
             * @brief
             *
             * @param root
             */
            static void LoadFromYaml(const YAML::Node& root);

            /**
             * @brief
             *
             * @param name
             * @return ConfigValBase::ptr
             */
            static ConfigValBase::ptr LookupBase(const std::string& name);

        private:
            static ConfigMap s_datas;
        };

        /**
         * @brief F 是源类型，T 是目标类型
         *
         */
        template<typename F, typename T>
        class LexicalCase {
        public:
            /**
             * @brief 类型转换
             *
             * @param value  源类型
             * @return T 目标类型
             */
            T operator() (const F& value) {
                return boost::lexical_cast<T>(value);
            }
        };

        /**
         * @brief 模板偏特化 将string 转化成std::vector<T>
         *
         * @tparam T
         */
        template<typename T>
        class LexicalCase<std::string, std::vector<T>> {
        public:
            std::vector<T> operator() (const std::string& value) {
                YAML::Node node = YAML::Load(value);
                typename std::vector<T> vec;
                std::stringstream ss;
                for (size_t i; i < node.size(); i++) {
                    ss.str("");
                    ss << node[i];
                    vec.push_back(LexicalCase<std::string, T>()(ss.str()));
                }
                return vec;
            }
        };

        /**
         * @brief 模板偏特化 将std::vector<T> 转化成 string
         *
         * @tparam T
         */
        template<typename T>
        class LexicalCase<std::vector<T>, std::string> {
        public:
            std::string operator ()(const std::vector<T>& value) {
                YAML::Node node(YAML::NodeType::Sequence);
                for (auto i : value) {
                    node.push_back(LexicalCase<T, std::string>()(value[i]));
                }
                std::stringstream ss;
                ss << node;
                return ss.str();
            }
        };

        /**
         * @brief 模板偏特化 将std::string 转换成std::list<T>
         *
         * @tparam T
         */
        template<typename T>
        class LexicalCase<std::string, std::list<T>> {
        public:
            std::list<T> operator() (const std::string& value) {
                YAML::Node node = YAML::Load(value);
                typename std::list<T> vec;
                std::stringstream ss;
                for (size_t i; i < node.size(); i++) {
                    ss.str("");
                    ss << node[i];
                    vec.push_back(LexicalCase<std::string, T>()(ss.str()));
                }
                return vec;
            }
        };

        /**
         * @brief 模板偏特化 将std::list<T> 转换成std::string
         *
         * @tparam T
         */
        template<typename T>
        class LexicalCase<std::list<T>, std::string> {
        public:
            std::string operator ()(const std::list<T>& value) {
                YAML::Node node(YAML::NodeType::Sequence);
                for (auto i : value) {
                    node.push_back(LexicalCase<T, std::string>()(i));
                }
                std::stringstream ss;
                ss << node;
                return ss.str();
            }
        };

        //TODO: string <-> set
        /**
         * @brief 模板偏特化 将std::string 转换成std::set<T>
         *
         * @tparam T
         */
        template<typename T>
        class LexicalCase<std::string, std::set<T>> {
        public:
            std::list<T> operator() (const std::string& value) {
                YAML::Node node = YAML::Load(value);
                typename std::list<T> vec;
                std::stringstream ss;
                for (size_t i; i < node.size(); i++) {
                    ss.str("");
                    ss << node[i];
                    vec.insert(LexicalCase<std::string, T>()(ss.str()));
                }
                return vec;
            }
        };

        /**
         * @brief 模板偏特化 将std::set<T> 转换成std::string
         *
         * @tparam T
         */
        template<typename T>
        class LexicalCase<std::set<T>, std::string> {
        public:
            std::string operator ()(const std::set<T>& value) {
                YAML::Node node(YAML::NodeType::Sequence);
                for (auto i : value) {
                    node.push_back(LexicalCase<T, std::string>()(i));
                }
                std::stringstream ss;
                ss << node;
                return ss.str();
            }
        };

        //TODO: string <-> map
        /**
         * @brief 模板偏特化 将std::string 转换成std::map<std::string,T>
         *
         * @tparam T
         */
        template<typename T>
        class LexicalCase<std::string, std::map<std::string, T>> {
        public:
            std::map<std::string, T> operator() (const std::string& value) {
                YAML::Node node = YAML::Load(value);
                typename std::map<std::string, T> vec;
                std::stringstream ss;
                for (auto it = node.begin();
                    it != node.end(); it++) {
                    ss.str("");
                    ss << it->second;
                    vec.insert(std::make_pair(it->first.Scalar(),
                        LexicalCase<std::string, T>()(ss.str())));
                }
                return vec;
            }
        };

        /**
         * @brief 模板偏特化 将std::map<std::string,T> 转换成std::string
         *
         * @tparam T
         */
        template<typename T>
        class LexicalCase<std::map<std::string, T>, std::string> {
        public:
            std::string operator ()(const std::map<std::string, T>& value) {
                YAML::Node node(YAML::NodeType::Map);
                for (auto& i : value) {
                    node[i.first] = YAML::Load(LexicalCase<T, std::string>()(i.second));
                }
                std::stringstream ss;
                ss << node;
                return ss.str();
            }
        };

        //TODO: string unordered_set
        /**
         * @brief 模板偏特化 将std::string 转换成 std::unordered_set<T>
         *
         * @tparam T
         */
        template<typename T>
        class LexicalCase<std::string, std::unordered_set<T>> {
        public:
            std::unordered_set<T> operator() (const std::string& value) {
                YAML::Node node = YAML::Load(value);
                typename std::unordered_set<T> vec;
                std::stringstream ss;
                for (size_t i; i < node.size(); i++) {
                    ss.str("");
                    ss << node[i];
                    vec.insert(LexicalCase<std::string, T>()(ss.str()));
                }
                return vec;
            }
        };

        /**
         * @brief 模板偏特化 将std::list<T> 转换成std::string
         *
         * @tparam T
         */
        template<typename T>
        class LexicalCase<std::unordered_set<T>, std::string> {
        public:
            std::string operator ()(const std::unordered_set<T>& value) {
                YAML::Node node(YAML::NodeType::Sequence);
                for (auto i : value) {
                    node.push_back(LexicalCase<T, std::string>()(value[i]));
                }
                std::stringstream ss;
                ss << node;
                return ss.str();
            }
        };

        //TODO: string <-> unordered_map
        /**
         * @brief 模板偏特化 将std::string 转换成std::list<T>
         *
         * @tparam T
         */
        template<typename T>
        class LexicalCase<std::string, std::unordered_map<std::string, T>> {
        public:
            std::unordered_map<std::string, T> operator() (const std::string& value) {
                YAML::Node node = YAML::Load(value);
                typename std::unordered_map<std::string, T> vec;
                std::stringstream ss;
                for (auto it = node.begin();
                    it != node.end(); it++) {
                    ss.str("");
                    ss << it->second;
                    vec.insert(std::make_pair(it->first.Scalar(),
                        LexicalCase<std::string, T>()(ss.str())));
                }
                return vec;
            }
        };

        /**
         * @brief 模板偏特化 将std::list<T> 转换成std::string
         *
         * @tparam T
         */
        template<typename T>
        class LexicalCase<std::unordered_map<std::string, T>, std::string> {
        public:
            std::string operator ()(const std::unordered_map<std::string, T>& value) {
                YAML::Node node(YAML::NodeType::Map);
                for (auto& it : value) {
                    node[it.first] = YAML::Load(LexicalCase<T, std::string>()(it.second));
                }
                std::stringstream ss;
                ss << node;
                return ss.str();
            }
        };

    }//namespace Conf
}//namespace Chen